package svm;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;

import svm.libsvm.svm;
import svm.libsvm.svm_model;
import svm.libsvm.svm_node;
import svm.libsvm.svm_parameter;

class svm_predict {
   private static double atof(final String s)
   {
      return Double.valueOf(s).doubleValue();
   }
   
   private static int atoi(final String s)
   {
      return Integer.parseInt(s);
   }
   
   private static void predict(final BufferedReader input, final DataOutputStream output, final svm_model model, final int predict_probability) throws IOException
   {
      int total = 0;
      double error = 0;
      double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0;
      
      final int svm_type = svm.svm_get_svm_type(model);
      final int nr_class = svm.svm_get_nr_class(model);
      double[] prob_estimates = null;
      
      if (predict_probability == 1)
      {
         if (svm_type == svm_parameter.EPSILON_SVR ||
               svm_type == svm_parameter.NU_SVR)
         {
            System.out.print("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma=" + svm.svm_get_svr_probability(model) + "\n");
         }
         else
         {
            final int[] labels = new int[nr_class];
            svm.svm_get_labels(model, labels);
            prob_estimates = new double[nr_class];
            output.writeBytes("labels");
            for (int j = 0; j < nr_class; j++) {
               output.writeBytes(" " + labels[j]);
            }
            output.writeBytes("\n");
         }
      }
      while (true)
      {
         final String line = input.readLine();
         if (line == null) {
            break;
         }
         
         final StringTokenizer st = new StringTokenizer(line, " \t\n\r\f:");
         
         final double target = atof(st.nextToken());
         final int m = st.countTokens() / 2;
         final svm_node[] x = new svm_node[m];
         for (int j = 0; j < m; j++)
         {
            x[j] = new svm_node();
            x[j].index = atoi(st.nextToken());
            x[j].value = atof(st.nextToken());
         }
         
         double v;
         if (predict_probability == 1 && (svm_type == svm_parameter.C_SVC || svm_type == svm_parameter.NU_SVC))
         {
            v = svm.svm_predict_probability(model, x, prob_estimates);
            output.writeBytes(v + " ");
            for (int j = 0; j < nr_class; j++) {
               output.writeBytes(prob_estimates[j] + " ");
            }
            output.writeBytes("\n");
         }
         else
         {
            v = svm.svm_predict(model, x);
            output.writeBytes(v + "\n");
         }
         
         if (v == target) {
         }
         error += (v - target) * (v - target);
         sumv += v;
         sumy += target;
         sumvv += v * v;
         sumyy += target * target;
         sumvy += v * target;
         ++total;
      }
      if (svm_type == svm_parameter.EPSILON_SVR ||
            svm_type == svm_parameter.NU_SVR)
      {
         System.out.print("Mean squared error = " + error / total + " (regression)\n");
         System.out.print("Squared correlation coefficient = " +
               (total * sumvy - sumv * sumy) * (total * sumvy - sumv * sumy) /
               ((total * sumvv - sumv * sumv) * (total * sumyy - sumy * sumy)) +
               " (regression)\n");
      }
      //else
      //System.out.print("Accuracy = "+(double)correct/total*100+
      // "% ("+correct+"/"+total+") (classification)\n");
   }
   
   private static void exit_with_help()
   {
      System.err.print("usage: svm_predict [options] test_file model_file output_file\n"
            + "options:\n"
            + "-b probability_estimates: whether to predict probability estimates, 0 or 1 (default 0); one-class SVM not supported yet\n");
      System.exit(1);
   }
   
   public static void run(final String argv[]) throws IOException
   {
      int i, predict_probability = 0;
      
      // parse options
      for (i = 0; i < argv.length; i++)
      {
         if (argv[i].charAt(0) != '-') {
            break;
         }
         ++i;
         switch (argv[i - 1].charAt(1))
         {
            case 'b':
               predict_probability = atoi(argv[i]);
               break;
            default:
               System.err.print("Unknown option: " + argv[i - 1] + "\n");
               exit_with_help();
         }
      }
      if (i >= argv.length - 2) {
         exit_with_help();
      }
      try
      {
         final BufferedReader input = new BufferedReader(new FileReader(argv[i]));
         final DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(argv[i + 2])));
         final svm_model model = svm.svm_load_model(argv[i + 1]);
         if (predict_probability == 1)
         {
            if (svm.svm_check_probability_model(model) == 0)
            {
               System.err.print("Model does not support probabiliy estimates\n");
               System.exit(1);
            }
         }
         else
         {
            if (svm.svm_check_probability_model(model) != 0)
            {
               System.out.print("Model supports probability estimates, but disabled in prediction.\n");
            }
         }
         predict(input, output, model, predict_probability);
         input.close();
         output.close();
      }
      catch (final FileNotFoundException e)
      {
         exit_with_help();
      }
      catch (final ArrayIndexOutOfBoundsException e)
      {
         exit_with_help();
      }
   }
}
